﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;

namespace ZMHWcfServiceLibrary
{
    // 注意: 使用“重构”菜单上的“重命名”命令，可以同时更改代码和配置文件中的接口名“ISubscriptionService”。
    [ServiceContract(CallbackContract = typeof(IMySSEvents))]
    public interface ISubscriptionService
    {
        [OperationContract]
        void Subscribe(string eventOperation);
        [OperationContract]
        void UnSubscribe(string eventOperation);
    }
    public interface IMySSEvents
    {
        [OperationContract(IsOneWay = true)]
        void OnEvent1();
        [OperationContract(IsOneWay = true)]
        void OnEvent2();
        [OperationContract(IsOneWay = true)]
        void OnEvent3();
    }
    public abstract class SubscriptionManager<T> where T : class
    {
        static Dictionary<string, List<T>> m_TransientStore;
        static SubscriptionManager()
        {
            m_TransientStore = new Dictionary<string, List<T>>();
            string[] methods = GetOperations();
            //Action<string> insert = delegate (string methodName)
            //{
            //    m_TransientStore.Add(methodName, new List<T>());
            //};
            Array.ForEach(methods, (methodName) => { m_TransientStore.Add(methodName, new List<T>()); });
        }

        internal static T[] GetTransientList(string eventOperation)
        {
            lock(typeof(SubscriptionManager<T>))
            {
                if(m_TransientStore.ContainsKey(eventOperation))
                {
                    List<T> list = m_TransientStore[eventOperation];
                    return list.ToArray();
                }
                return new T[] { };
            }
        }

        internal static T[] GetPersistentList(string eventOperation)
        {
            string[] addresses = GetSubscribersToContractEventOperation(typeof(T).ToString(), eventOperation);
            List<T> subscribers = new List<T>(addresses.Length);
            foreach (string address in addresses)
            {
                Binding binding = GetBindingFromAddress(address);
                T proxy = ChannelFactory<T>.CreateChannel(binding, new EndpointAddress(address));
                subscribers.Add(proxy);
            }
            return subscribers.ToArray();
        }

        private static Binding GetBindingFromAddress(string address)
        {
            throw new NotImplementedException();
        }

        private static string[] GetSubscribersToContractEventOperation(string v, string eventOperation)
        {
            throw new NotImplementedException();
        }

        private static string[] GetOperations()
        {
            MethodInfo[] methods = typeof(T).GetMethods(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance);
            List<string> operations = new List<string>(methods.Length);
            Array.ForEach(methods, (method) => { Debug.Assert(!operations.Contains(method.Name)); operations.Add(method.Name); });
            return operations.ToArray();
        }
        public void Subscribe(string eventOperation)
        {
            lock (typeof(SubscriptionManager<T>))
            {
                T subscriber = OperationContext.Current.GetCallbackChannel<T>();
                if (!string.IsNullOrEmpty(eventOperation)) AddTransient(subscriber, eventOperation);
                else
                {
                    string[] methods = GetOperations();
                    Array.ForEach(methods, (methodName) => { AddTransient(subscriber, methodName); });
                }
            }
        }
        static void AddTransient(T subscriber, string eventOperation)
        {
            lock (typeof(SubscriptionManager<T>))
            {
                List<T> list = m_TransientStore[eventOperation];
                if (list.Contains(subscriber)) return;
                list.Add(subscriber);
            }
        }

        public void UnSubscribe(string eventOperation)
        {
            lock (typeof(SubscriptionManager<T>))
            {
                T subscriber = OperationContext.Current.GetCallbackChannel<T>();
                if (!string.IsNullOrEmpty(eventOperation)) RemoveTransient(subscriber, eventOperation);
                else
                {
                    Array.ForEach(GetOperations(), (methodName) => { RemoveTransient(subscriber, methodName); });
                }
            }
        }
        static void RemoveTransient(T subscriber, string eventOperation)
        {
            lock (typeof(SubscriptionManager<T>))
            {
                List<T> list = m_TransientStore[eventOperation];
                list.Remove(subscriber);
            }
        }
        [OperationBehavior(TransactionScopeRequired = true)]
        public void Subscribe(string address, string eventsContract, string eventOperation)
        {
            if (!string.IsNullOrEmpty(eventOperation)) AddTransient(address, eventsContract, eventOperation);
            else Array.ForEach(GetOperations(), (methodName) => { AddTransient(address, eventsContract, methodName); });
        }
        static void AddTransient(string address, string eventsContract, string eventOperation)
        {
            //ado.net sql server store and subscribe
        }

        [OperationBehavior(TransactionScopeRequired = true)]
        public void UnSubscribe(string address, string eventsContract, string eventOperation)
        {
            if (!string.IsNullOrEmpty(eventOperation)) RemoveTransient(address, eventsContract, eventOperation);
            else Array.ForEach(GetOperations(), (methodName) => { RemoveTransient(address, eventsContract, methodName); });
        } 
        static void RemoveTransient(string address, string eventsContract, string eventOperation)
        {
            //ado.net sql server store and subscribe
        }
    }
    public interface IPersistentSubscriptionService
    {
        [OperationContract]
        [TransactionFlow(TransactionFlowOption.Allowed)]
        void Subscribe(string address, string eventsContract, string eventOperation);
        [OperationContract]
        [TransactionFlow(TransactionFlowOption.Allowed)]
        void UnSubscribe(string address, string eventsContract, string eventOperation);
    }

    public abstract class PublishService<T> where T:class
    {
        protected static void FireEvent(params object[] args)
        {

        }
    }
      
}
